import org.antlr.runtime.*;
import org.antlr.runtime.tree.*;
import org.antlr.stringtemplate.*;
import java.io.FileInputStream;
import java.io.ByteArrayInputStream;
import java.io.SequenceInputStream;
import java.io.InputStream;
import java.util.ArrayList;

/**
 * The main executable for the Graphr Language. Starts the lexer, parser, and walker.
 * @author Paul Dix
 */
public class Graphr {
	/**
	 * Main method for Graphr. First walks all include files, then walks the main program.
	 * @param args - the arguments to the main function. Can either be of the form: <br><ol><li>-e "some string"</li><li>some_script.gr</li></ol>
	 */
    public static void main(String[] args) throws Exception {
      ANTLRInputStream input = null;
      boolean executeSingleCommand = false;
      if (args[0].equals("-e")) {
        input = new ANTLRInputStream(new ByteArrayInputStream(args[1].getBytes()));
        executeSingleCommand = true;
      }
      else {
        input = new ANTLRInputStream(new FileInputStream(args[0]));
      }
      // Create a lexer attached to that input stream
      GraphrLexer lexer = new GraphrLexer(input);
      // Create a stream of tokens pulled from the lexer
      CommonTokenStream tokens = new CommonTokenStream(lexer);
      // Create a parser attached to the token stream
      GraphrParser parser = new GraphrParser(tokens);
      // Invoke the program rule in get return value
      GraphrParser.program_return r = parser.program();
        if (!executeSingleCommand) {
          /* 
           * First we'll walk to look for includes and combine all file streams into one
          */

          // here's the combining the filestreams part
          ArrayList<FileInputStream> fileStreams = new ArrayList<FileInputStream>();
          for (String fileName : parser.filesToInclude) {
            fileStreams.add(new FileInputStream(fileName));
          }
          fileStreams.add(new FileInputStream(args[0]));
          InputStream allFiles = getStream(fileStreams);

          // and now we have a single stream with everything in it.
          // Create a lexer attached to that input stream
          lexer = new GraphrLexer(new ANTLRInputStream(allFiles));
          // Create a stream of tokens pulled from the lexer
          tokens = new CommonTokenStream(lexer);

          // Create a parser attached to the token stream
          parser = new GraphrParser(tokens);

          // Invoke the program rule in get return value
          r = parser.program();
          
        }
        
        CommonTree t = (CommonTree)r.getTree();

        // Walk resulting tree; create treenode stream first
        CommonTreeNodeStream nodes = new CommonTreeNodeStream(t);
        // AST nodes have payloads that point into token stream
        nodes.setTokenStream(tokens); 
        // Create a tree Walker attached to the nodes stream
        GraphrWalker walker = new GraphrWalker(nodes);
        // Invoke the start symbol, rule program
        walker.functions = parser.functions;
        walker.program();
    }
    
    /**
     * Method to combine multiple input streams into one stream.<br>
     * This is neccessary for combining the input streams generated for each include file.
     * @param fileStreams - the array list of FileInputStreams to combine.
     */
    public static InputStream getStream(ArrayList<FileInputStream> fileStreams) {
      if (fileStreams.size() == 1) {
        return fileStreams.get(0);
      }
      else if (fileStreams.size() == 2) {
        return new SequenceInputStream(fileStreams.get(0), fileStreams.get(1));
      }
      else {
        FileInputStream st = fileStreams.get(0);
        fileStreams.remove(0);
        return new SequenceInputStream(st, getStream(fileStreams));
      }
    }
}